home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / imaplib.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  29KB  |  981 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. __version__ = '2.58'
  5. import binascii
  6. import os
  7. import random
  8. import re
  9. import socket
  10. import sys
  11. import time
  12. __all__ = [
  13.     'IMAP4',
  14.     'IMAP4_SSL',
  15.     'IMAP4_stream',
  16.     'Internaldate2tuple',
  17.     'Int2AP',
  18.     'ParseFlags',
  19.     'Time2Internaldate']
  20. CRLF = '\r\n'
  21. Debug = 0
  22. IMAP4_PORT = 143
  23. IMAP4_SSL_PORT = 993
  24. AllowedVersions = ('IMAP4REV1', 'IMAP4')
  25. Commands = {
  26.     'APPEND': ('AUTH', 'SELECTED'),
  27.     'AUTHENTICATE': ('NONAUTH',),
  28.     'CAPABILITY': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
  29.     'CHECK': ('SELECTED',),
  30.     'CLOSE': ('SELECTED',),
  31.     'COPY': ('SELECTED',),
  32.     'CREATE': ('AUTH', 'SELECTED'),
  33.     'DELETE': ('AUTH', 'SELECTED'),
  34.     'DELETEACL': ('AUTH', 'SELECTED'),
  35.     'EXAMINE': ('AUTH', 'SELECTED'),
  36.     'EXPUNGE': ('SELECTED',),
  37.     'FETCH': ('SELECTED',),
  38.     'GETACL': ('AUTH', 'SELECTED'),
  39.     'GETANNOTATION': ('AUTH', 'SELECTED'),
  40.     'GETQUOTA': ('AUTH', 'SELECTED'),
  41.     'GETQUOTAROOT': ('AUTH', 'SELECTED'),
  42.     'MYRIGHTS': ('AUTH', 'SELECTED'),
  43.     'LIST': ('AUTH', 'SELECTED'),
  44.     'LOGIN': ('NONAUTH',),
  45.     'LOGOUT': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
  46.     'LSUB': ('AUTH', 'SELECTED'),
  47.     'NAMESPACE': ('AUTH', 'SELECTED'),
  48.     'NOOP': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
  49.     'PARTIAL': ('SELECTED',),
  50.     'PROXYAUTH': ('AUTH',),
  51.     'RENAME': ('AUTH', 'SELECTED'),
  52.     'SEARCH': ('SELECTED',),
  53.     'SELECT': ('AUTH', 'SELECTED'),
  54.     'SETACL': ('AUTH', 'SELECTED'),
  55.     'SETANNOTATION': ('AUTH', 'SELECTED'),
  56.     'SETQUOTA': ('AUTH', 'SELECTED'),
  57.     'SORT': ('SELECTED',),
  58.     'STATUS': ('AUTH', 'SELECTED'),
  59.     'STORE': ('SELECTED',),
  60.     'SUBSCRIBE': ('AUTH', 'SELECTED'),
  61.     'THREAD': ('SELECTED',),
  62.     'UID': ('SELECTED',),
  63.     'UNSUBSCRIBE': ('AUTH', 'SELECTED') }
  64. Continuation = re.compile('\\+( (?P<data>.*))?')
  65. Flags = re.compile('.*FLAGS \\((?P<flags>[^\\)]*)\\)')
  66. InternalDate = re.compile('.*INTERNALDATE "(?P<day>[ 0123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9]) (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9]) (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])"')
  67. Literal = re.compile('.*{(?P<size>\\d+)}$')
  68. MapCRLF = re.compile('\\r\\n|\\r|\\n')
  69. Response_code = re.compile('\\[(?P<type>[A-Z-]+)( (?P<data>[^\\]]*))?\\]')
  70. Untagged_response = re.compile('\\* (?P<type>[A-Z-]+)( (?P<data>.*))?')
  71. Untagged_status = re.compile('\\* (?P<data>\\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?')
  72.  
  73. class IMAP4:
  74.     
  75.     class error(Exception):
  76.         pass
  77.  
  78.     
  79.     class abort(error):
  80.         pass
  81.  
  82.     
  83.     class readonly(abort):
  84.         pass
  85.  
  86.     mustquote = re.compile("[^\\w!#$%&'*+,.:;<=>?^`|~-]")
  87.     
  88.     def __init__(self, host = '', port = IMAP4_PORT):
  89.         self.debug = Debug
  90.         self.state = 'LOGOUT'
  91.         self.literal = None
  92.         self.tagged_commands = { }
  93.         self.untagged_responses = { }
  94.         self.continuation_response = ''
  95.         self.is_readonly = False
  96.         self.tagnum = 0
  97.         self.open(host, port)
  98.         self.tagpre = Int2AP(random.randint(4096, 65535))
  99.         self.tagre = re.compile('(?P<tag>' + self.tagpre + '\\d+) (?P<type>[A-Z]+) (?P<data>.*)')
  100.         self.welcome = self._get_response()
  101.         if 'PREAUTH' in self.untagged_responses:
  102.             self.state = 'AUTH'
  103.         elif 'OK' in self.untagged_responses:
  104.             self.state = 'NONAUTH'
  105.         else:
  106.             raise self.error(self.welcome)
  107.         (typ, dat) = self.capability()
  108.         if dat == [
  109.             None]:
  110.             raise self.error('no CAPABILITY response from server')
  111.         
  112.         self.capabilities = tuple(dat[-1].upper().split())
  113.         for version in AllowedVersions:
  114.             if version not in self.capabilities:
  115.                 continue
  116.             
  117.             self.PROTOCOL_VERSION = version
  118.             return None
  119.         
  120.         raise self.error('server not IMAP4 compliant')
  121.  
  122.     
  123.     def __getattr__(self, attr):
  124.         if attr in Commands:
  125.             return getattr(self, attr.lower())
  126.         
  127.         raise AttributeError("Unknown IMAP4 command: '%s'" % attr)
  128.  
  129.     
  130.     def open(self, host = '', port = IMAP4_PORT):
  131.         self.host = host
  132.         self.port = port
  133.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  134.         self.sock.connect((host, port))
  135.         self.file = self.sock.makefile('rb')
  136.  
  137.     
  138.     def read(self, size):
  139.         return self.file.read(size)
  140.  
  141.     
  142.     def readline(self):
  143.         return self.file.readline()
  144.  
  145.     
  146.     def send(self, data):
  147.         self.sock.sendall(data)
  148.  
  149.     
  150.     def shutdown(self):
  151.         self.file.close()
  152.         self.sock.close()
  153.  
  154.     
  155.     def socket(self):
  156.         return self.sock
  157.  
  158.     
  159.     def recent(self):
  160.         name = 'RECENT'
  161.         (typ, dat) = self._untagged_response('OK', [
  162.             None], name)
  163.         if dat[-1]:
  164.             return (typ, dat)
  165.         
  166.         (typ, dat) = self.noop()
  167.         return self._untagged_response(typ, dat, name)
  168.  
  169.     
  170.     def response(self, code):
  171.         return self._untagged_response(code, [
  172.             None], code.upper())
  173.  
  174.     
  175.     def append(self, mailbox, flags, date_time, message):
  176.         name = 'APPEND'
  177.         if not mailbox:
  178.             mailbox = 'INBOX'
  179.         
  180.         if flags:
  181.             if (flags[0], flags[-1]) != ('(', ')'):
  182.                 flags = '(%s)' % flags
  183.             
  184.         else:
  185.             flags = None
  186.         if date_time:
  187.             date_time = Time2Internaldate(date_time)
  188.         else:
  189.             date_time = None
  190.         self.literal = MapCRLF.sub(CRLF, message)
  191.         return self._simple_command(name, mailbox, flags, date_time)
  192.  
  193.     
  194.     def authenticate(self, mechanism, authobject):
  195.         mech = mechanism.upper()
  196.         self.literal = _Authenticator(authobject).process
  197.         (typ, dat) = self._simple_command('AUTHENTICATE', mech)
  198.         if typ != 'OK':
  199.             raise self.error(dat[-1])
  200.         
  201.         self.state = 'AUTH'
  202.         return (typ, dat)
  203.  
  204.     
  205.     def capability(self):
  206.         name = 'CAPABILITY'
  207.         (typ, dat) = self._simple_command(name)
  208.         return self._untagged_response(typ, dat, name)
  209.  
  210.     
  211.     def check(self):
  212.         return self._simple_command('CHECK')
  213.  
  214.     
  215.     def close(self):
  216.         
  217.         try:
  218.             (typ, dat) = self._simple_command('CLOSE')
  219.         finally:
  220.             self.state = 'AUTH'
  221.  
  222.         return (typ, dat)
  223.  
  224.     
  225.     def copy(self, message_set, new_mailbox):
  226.         return self._simple_command('COPY', message_set, new_mailbox)
  227.  
  228.     
  229.     def create(self, mailbox):
  230.         return self._simple_command('CREATE', mailbox)
  231.  
  232.     
  233.     def delete(self, mailbox):
  234.         return self._simple_command('DELETE', mailbox)
  235.  
  236.     
  237.     def deleteacl(self, mailbox, who):
  238.         return self._simple_command('DELETEACL', mailbox, who)
  239.  
  240.     
  241.     def expunge(self):
  242.         name = 'EXPUNGE'
  243.         (typ, dat) = self._simple_command(name)
  244.         return self._untagged_response(typ, dat, name)
  245.  
  246.     
  247.     def fetch(self, message_set, message_parts):
  248.         name = 'FETCH'
  249.         (typ, dat) = self._simple_command(name, message_set, message_parts)
  250.         return self._untagged_response(typ, dat, name)
  251.  
  252.     
  253.     def getacl(self, mailbox):
  254.         (typ, dat) = self._simple_command('GETACL', mailbox)
  255.         return self._untagged_response(typ, dat, 'ACL')
  256.  
  257.     
  258.     def getannotation(self, mailbox, entry, attribute):
  259.         (typ, dat) = self._simple_command('GETANNOTATION', mailbox, entry, attribute)
  260.         return self._untagged_response(typ, dat, 'ANNOTATION')
  261.  
  262.     
  263.     def getquota(self, root):
  264.         (typ, dat) = self._simple_command('GETQUOTA', root)
  265.         return self._untagged_response(typ, dat, 'QUOTA')
  266.  
  267.     
  268.     def getquotaroot(self, mailbox):
  269.         (typ, dat) = self._simple_command('GETQUOTAROOT', mailbox)
  270.         (typ, quota) = self._untagged_response(typ, dat, 'QUOTA')
  271.         (typ, quotaroot) = self._untagged_response(typ, dat, 'QUOTAROOT')
  272.         return (typ, [
  273.             quotaroot,
  274.             quota])
  275.  
  276.     
  277.     def list(self, directory = '""', pattern = '*'):
  278.         name = 'LIST'
  279.         (typ, dat) = self._simple_command(name, directory, pattern)
  280.         return self._untagged_response(typ, dat, name)
  281.  
  282.     
  283.     def login(self, user, password):
  284.         (typ, dat) = self._simple_command('LOGIN', user, self._quote(password))
  285.         if typ != 'OK':
  286.             raise self.error(dat[-1])
  287.         
  288.         self.state = 'AUTH'
  289.         return (typ, dat)
  290.  
  291.     
  292.     def login_cram_md5(self, user, password):
  293.         self.user = user
  294.         self.password = password
  295.         return self.authenticate('CRAM-MD5', self._CRAM_MD5_AUTH)
  296.  
  297.     
  298.     def _CRAM_MD5_AUTH(self, challenge):
  299.         import hmac as hmac
  300.         return self.user + ' ' + hmac.HMAC(self.password, challenge).hexdigest()
  301.  
  302.     
  303.     def logout(self):
  304.         self.state = 'LOGOUT'
  305.         
  306.         try:
  307.             (typ, dat) = self._simple_command('LOGOUT')
  308.         except:
  309.             typ = 'NO'
  310.             dat = [
  311.                 '%s: %s' % sys.exc_info()[:2]]
  312.  
  313.         self.shutdown()
  314.         if 'BYE' in self.untagged_responses:
  315.             return ('BYE', self.untagged_responses['BYE'])
  316.         
  317.         return (typ, dat)
  318.  
  319.     
  320.     def lsub(self, directory = '""', pattern = '*'):
  321.         name = 'LSUB'
  322.         (typ, dat) = self._simple_command(name, directory, pattern)
  323.         return self._untagged_response(typ, dat, name)
  324.  
  325.     
  326.     def myrights(self, mailbox):
  327.         (typ, dat) = self._simple_command('MYRIGHTS', mailbox)
  328.         return self._untagged_response(typ, dat, 'MYRIGHTS')
  329.  
  330.     
  331.     def namespace(self):
  332.         name = 'NAMESPACE'
  333.         (typ, dat) = self._simple_command(name)
  334.         return self._untagged_response(typ, dat, name)
  335.  
  336.     
  337.     def noop(self):
  338.         return self._simple_command('NOOP')
  339.  
  340.     
  341.     def partial(self, message_num, message_part, start, length):
  342.         name = 'PARTIAL'
  343.         (typ, dat) = self._simple_command(name, message_num, message_part, start, length)
  344.         return self._untagged_response(typ, dat, 'FETCH')
  345.  
  346.     
  347.     def proxyauth(self, user):
  348.         name = 'PROXYAUTH'
  349.         return self._simple_command('PROXYAUTH', user)
  350.  
  351.     
  352.     def rename(self, oldmailbox, newmailbox):
  353.         return self._simple_command('RENAME', oldmailbox, newmailbox)
  354.  
  355.     
  356.     def search(self, charset, *criteria):
  357.         name = 'SEARCH'
  358.         if charset:
  359.             (typ, dat) = self._simple_command(name, 'CHARSET', charset, *criteria)
  360.         else:
  361.             (typ, dat) = self._simple_command(name, *criteria)
  362.         return self._untagged_response(typ, dat, name)
  363.  
  364.     
  365.     def select(self, mailbox = 'INBOX', readonly = False):
  366.         self.untagged_responses = { }
  367.         self.is_readonly = readonly
  368.         if readonly:
  369.             name = 'EXAMINE'
  370.         else:
  371.             name = 'SELECT'
  372.         (typ, dat) = self._simple_command(name, mailbox)
  373.         if typ != 'OK':
  374.             self.state = 'AUTH'
  375.             return (typ, dat)
  376.         
  377.         self.state = 'SELECTED'
  378.         if 'READ-ONLY' in self.untagged_responses and not readonly:
  379.             raise self.readonly('%s is not writable' % mailbox)
  380.         
  381.         return (typ, self.untagged_responses.get('EXISTS', [
  382.             None]))
  383.  
  384.     
  385.     def setacl(self, mailbox, who, what):
  386.         return self._simple_command('SETACL', mailbox, who, what)
  387.  
  388.     
  389.     def setannotation(self, *args):
  390.         (typ, dat) = self._simple_command('SETANNOTATION', *args)
  391.         return self._untagged_response(typ, dat, 'ANNOTATION')
  392.  
  393.     
  394.     def setquota(self, root, limits):
  395.         (typ, dat) = self._simple_command('SETQUOTA', root, limits)
  396.         return self._untagged_response(typ, dat, 'QUOTA')
  397.  
  398.     
  399.     def sort(self, sort_criteria, charset, *search_criteria):
  400.         name = 'SORT'
  401.         if (sort_criteria[0], sort_criteria[-1]) != ('(', ')'):
  402.             sort_criteria = '(%s)' % sort_criteria
  403.         
  404.         (typ, dat) = self._simple_command(name, sort_criteria, charset, *search_criteria)
  405.         return self._untagged_response(typ, dat, name)
  406.  
  407.     
  408.     def status(self, mailbox, names):
  409.         name = 'STATUS'
  410.         (typ, dat) = self._simple_command(name, mailbox, names)
  411.         return self._untagged_response(typ, dat, name)
  412.  
  413.     
  414.     def store(self, message_set, command, flags):
  415.         if (flags[0], flags[-1]) != ('(', ')'):
  416.             flags = '(%s)' % flags
  417.         
  418.         (typ, dat) = self._simple_command('STORE', message_set, command, flags)
  419.         return self._untagged_response(typ, dat, 'FETCH')
  420.  
  421.     
  422.     def subscribe(self, mailbox):
  423.         return self._simple_command('SUBSCRIBE', mailbox)
  424.  
  425.     
  426.     def thread(self, threading_algorithm, charset, *search_criteria):
  427.         name = 'THREAD'
  428.         (typ, dat) = self._simple_command(name, threading_algorithm, charset, *search_criteria)
  429.         return self._untagged_response(typ, dat, name)
  430.  
  431.     
  432.     def uid(self, command, *args):
  433.         command = command.upper()
  434.         if command not in Commands:
  435.             raise self.error('Unknown IMAP4 UID command: %s' % command)
  436.         
  437.         if self.state not in Commands[command]:
  438.             raise self.error('command %s illegal in state %s' % (command, self.state))
  439.         
  440.         name = 'UID'
  441.         (typ, dat) = self._simple_command(name, command, *args)
  442.         if command in ('SEARCH', 'SORT'):
  443.             name = command
  444.         else:
  445.             name = 'FETCH'
  446.         return self._untagged_response(typ, dat, name)
  447.  
  448.     
  449.     def unsubscribe(self, mailbox):
  450.         return self._simple_command('UNSUBSCRIBE', mailbox)
  451.  
  452.     
  453.     def xatom(self, name, *args):
  454.         name = name.upper()
  455.         if name not in Commands:
  456.             Commands[name] = (self.state,)
  457.         
  458.         return self._simple_command(name, *args)
  459.  
  460.     
  461.     def _append_untagged(self, typ, dat):
  462.         if dat is None:
  463.             dat = ''
  464.         
  465.         ur = self.untagged_responses
  466.         if typ in ur:
  467.             ur[typ].append(dat)
  468.         else:
  469.             ur[typ] = [
  470.                 dat]
  471.  
  472.     
  473.     def _check_bye(self):
  474.         bye = self.untagged_responses.get('BYE')
  475.         if bye:
  476.             raise self.abort(bye[-1])
  477.         
  478.  
  479.     
  480.     def _command(self, name, *args):
  481.         if self.state not in Commands[name]:
  482.             self.literal = None
  483.             raise self.error('command %s illegal in state %s' % (name, self.state))
  484.         
  485.         for typ in ('OK', 'NO', 'BAD'):
  486.             if typ in self.untagged_responses:
  487.                 del self.untagged_responses[typ]
  488.                 continue
  489.         
  490.         if 'READ-ONLY' in self.untagged_responses and not (self.is_readonly):
  491.             raise self.readonly('mailbox status changed to READ-ONLY')
  492.         
  493.         tag = self._new_tag()
  494.         data = '%s %s' % (tag, name)
  495.         for arg in args:
  496.             if arg is None:
  497.                 continue
  498.             
  499.             data = '%s %s' % (data, self._checkquote(arg))
  500.         
  501.         literal = self.literal
  502.         if literal is not None:
  503.             self.literal = None
  504.             if type(literal) is type(self._command):
  505.                 literator = literal
  506.             else:
  507.                 literator = None
  508.                 data = '%s {%s}' % (data, len(literal))
  509.         
  510.         
  511.         try:
  512.             self.send('%s%s' % (data, CRLF))
  513.         except (socket.error, OSError):
  514.             val = None
  515.             raise self.abort('socket error: %s' % val)
  516.  
  517.         if literal is None:
  518.             return tag
  519.         
  520.         while None:
  521.             while self._get_response():
  522.                 if self.tagged_commands[tag]:
  523.                     return tag
  524.                     continue
  525.             if literator:
  526.                 literal = literator(self.continuation_response)
  527.             
  528.             
  529.             try:
  530.                 self.send(literal)
  531.                 self.send(CRLF)
  532.             except (socket.error, OSError):
  533.                 val = None
  534.                 raise self.abort('socket error: %s' % val)
  535.  
  536.             if not literator:
  537.                 break
  538.                 continue
  539.             continue
  540.             return tag
  541.  
  542.     
  543.     def _command_complete(self, name, tag):
  544.         self._check_bye()
  545.         
  546.         try:
  547.             (typ, data) = self._get_tagged_response(tag)
  548.         except self.abort:
  549.             val = None
  550.             raise self.abort('command: %s => %s' % (name, val))
  551.         except self.error:
  552.             val = None
  553.             raise self.error('command: %s => %s' % (name, val))
  554.  
  555.         self._check_bye()
  556.         if typ == 'BAD':
  557.             raise self.error('%s command error: %s %s' % (name, typ, data))
  558.         
  559.         return (typ, data)
  560.  
  561.     
  562.     def _get_response(self):
  563.         resp = self._get_line()
  564.         if self._match(self.tagre, resp):
  565.             tag = self.mo.group('tag')
  566.             if tag not in self.tagged_commands:
  567.                 raise self.abort('unexpected tagged response: %s' % resp)
  568.             
  569.             typ = self.mo.group('type')
  570.             dat = self.mo.group('data')
  571.             self.tagged_commands[tag] = (typ, [
  572.                 dat])
  573.         else:
  574.             dat2 = None
  575.             if not self._match(Untagged_response, resp):
  576.                 if self._match(Untagged_status, resp):
  577.                     dat2 = self.mo.group('data2')
  578.                 
  579.             
  580.             if self.mo is None:
  581.                 if self._match(Continuation, resp):
  582.                     self.continuation_response = self.mo.group('data')
  583.                     return None
  584.                 
  585.                 raise self.abort("unexpected response: '%s'" % resp)
  586.             
  587.             typ = self.mo.group('type')
  588.             dat = self.mo.group('data')
  589.             if dat is None:
  590.                 dat = ''
  591.             
  592.             if dat2:
  593.                 dat = dat + ' ' + dat2
  594.             
  595.             while self._match(Literal, dat):
  596.                 size = int(self.mo.group('size'))
  597.                 data = self.read(size)
  598.                 self._append_untagged(typ, (dat, data))
  599.                 dat = self._get_line()
  600.             self._append_untagged(typ, dat)
  601.         if typ in ('OK', 'NO', 'BAD') and self._match(Response_code, dat):
  602.             self._append_untagged(self.mo.group('type'), self.mo.group('data'))
  603.         
  604.         return resp
  605.  
  606.     
  607.     def _get_tagged_response(self, tag):
  608.         while None:
  609.             result = self.tagged_commands[tag]
  610.             if result is not None:
  611.                 del self.tagged_commands[tag]
  612.                 return result
  613.             
  614.             
  615.             try:
  616.                 self._get_response()
  617.             continue
  618.             except self.abort:
  619.                 val = None
  620.                 raise 
  621.                 continue
  622.             
  623.  
  624.             return None
  625.  
  626.     
  627.     def _get_line(self):
  628.         line = self.readline()
  629.         if not line:
  630.             raise self.abort('socket error: EOF')
  631.         
  632.         line = line[:-2]
  633.         return line
  634.  
  635.     
  636.     def _match(self, cre, s):
  637.         self.mo = cre.match(s)
  638.         return self.mo is not None
  639.  
  640.     
  641.     def _new_tag(self):
  642.         tag = '%s%s' % (self.tagpre, self.tagnum)
  643.         self.tagnum = self.tagnum + 1
  644.         self.tagged_commands[tag] = None
  645.         return tag
  646.  
  647.     
  648.     def _checkquote(self, arg):
  649.         if type(arg) is not type(''):
  650.             return arg
  651.         
  652.         if len(arg) >= 2 and (arg[0], arg[-1]) in (('(', ')'), ('"', '"')):
  653.             return arg
  654.         
  655.         if arg and self.mustquote.search(arg) is None:
  656.             return arg
  657.         
  658.         return self._quote(arg)
  659.  
  660.     
  661.     def _quote(self, arg):
  662.         arg = arg.replace('\\', '\\\\')
  663.         arg = arg.replace('"', '\\"')
  664.         return '"%s"' % arg
  665.  
  666.     
  667.     def _simple_command(self, name, *args):
  668.         return self._command_complete(name, self._command(name, *args))
  669.  
  670.     
  671.     def _untagged_response(self, typ, dat, name):
  672.         if typ == 'NO':
  673.             return (typ, dat)
  674.         
  675.         if name not in self.untagged_responses:
  676.             return (typ, [
  677.                 None])
  678.         
  679.         data = self.untagged_responses.pop(name)
  680.         return (typ, data)
  681.  
  682.  
  683.  
  684. class IMAP4_SSL(IMAP4):
  685.     
  686.     def __init__(self, host = '', port = IMAP4_SSL_PORT, keyfile = None, certfile = None):
  687.         self.keyfile = keyfile
  688.         self.certfile = certfile
  689.         IMAP4.__init__(self, host, port)
  690.  
  691.     
  692.     def open(self, host = '', port = IMAP4_SSL_PORT):
  693.         self.host = host
  694.         self.port = port
  695.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  696.         self.sock.connect((host, port))
  697.         self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
  698.  
  699.     
  700.     def read(self, size):
  701.         chunks = []
  702.         read = 0
  703.         while read < size:
  704.             data = self.sslobj.read(min(size - read, 16384))
  705.             read += len(data)
  706.             chunks.append(data)
  707.         return ''.join(chunks)
  708.  
  709.     
  710.     def readline(self):
  711.         line = []
  712.         while None:
  713.             char = self.sslobj.read(1)
  714.             if char == '\n':
  715.                 return ''.join(line)
  716.                 continue
  717.             continue
  718.             return None
  719.  
  720.     
  721.     def send(self, data):
  722.         bytes = len(data)
  723.         while bytes > 0:
  724.             sent = self.sslobj.write(data)
  725.             if sent == bytes:
  726.                 break
  727.             
  728.             data = data[sent:]
  729.             bytes = bytes - sent
  730.  
  731.     
  732.     def shutdown(self):
  733.         self.sock.close()
  734.  
  735.     
  736.     def socket(self):
  737.         return self.sock
  738.  
  739.     
  740.     def ssl(self):
  741.         return self.sslobj
  742.  
  743.  
  744.  
  745. class IMAP4_stream(IMAP4):
  746.     
  747.     def __init__(self, command):
  748.         self.command = command
  749.         IMAP4.__init__(self)
  750.  
  751.     
  752.     def open(self, host = None, port = None):
  753.         self.host = None
  754.         self.port = None
  755.         self.sock = None
  756.         self.file = None
  757.         (self.writefile, self.readfile) = os.popen2(self.command)
  758.  
  759.     
  760.     def read(self, size):
  761.         return self.readfile.read(size)
  762.  
  763.     
  764.     def readline(self):
  765.         return self.readfile.readline()
  766.  
  767.     
  768.     def send(self, data):
  769.         self.writefile.write(data)
  770.         self.writefile.flush()
  771.  
  772.     
  773.     def shutdown(self):
  774.         self.readfile.close()
  775.         self.writefile.close()
  776.  
  777.  
  778.  
  779. class _Authenticator:
  780.     
  781.     def __init__(self, mechinst):
  782.         self.mech = mechinst
  783.  
  784.     
  785.     def process(self, data):
  786.         ret = self.mech(self.decode(data))
  787.         if ret is None:
  788.             return '*'
  789.         
  790.         return self.encode(ret)
  791.  
  792.     
  793.     def encode(self, inp):
  794.         oup = ''
  795.         while inp:
  796.             if len(inp) > 48:
  797.                 t = inp[:48]
  798.                 inp = inp[48:]
  799.             else:
  800.                 t = inp
  801.                 inp = ''
  802.             e = binascii.b2a_base64(t)
  803.             if e:
  804.                 oup = oup + e[:-1]
  805.                 continue
  806.         return oup
  807.  
  808.     
  809.     def decode(self, inp):
  810.         if not inp:
  811.             return ''
  812.         
  813.         return binascii.a2b_base64(inp)
  814.  
  815.  
  816. Mon2num = {
  817.     'Jan': 1,
  818.     'Feb': 2,
  819.     'Mar': 3,
  820.     'Apr': 4,
  821.     'May': 5,
  822.     'Jun': 6,
  823.     'Jul': 7,
  824.     'Aug': 8,
  825.     'Sep': 9,
  826.     'Oct': 10,
  827.     'Nov': 11,
  828.     'Dec': 12 }
  829.  
  830. def Internaldate2tuple(resp):
  831.     mo = InternalDate.match(resp)
  832.     if not mo:
  833.         return None
  834.     
  835.     mon = Mon2num[mo.group('mon')]
  836.     zonen = mo.group('zonen')
  837.     day = int(mo.group('day'))
  838.     year = int(mo.group('year'))
  839.     hour = int(mo.group('hour'))
  840.     min = int(mo.group('min'))
  841.     sec = int(mo.group('sec'))
  842.     zoneh = int(mo.group('zoneh'))
  843.     zonem = int(mo.group('zonem'))
  844.     zone = (zoneh * 60 + zonem) * 60
  845.     if zonen == '-':
  846.         zone = -zone
  847.     
  848.     tt = (year, mon, day, hour, min, sec, -1, -1, -1)
  849.     utc = time.mktime(tt)
  850.     lt = time.localtime(utc)
  851.     if time.daylight and lt[-1]:
  852.         zone = zone + time.altzone
  853.     else:
  854.         zone = zone + time.timezone
  855.     return time.localtime(utc - zone)
  856.  
  857.  
  858. def Int2AP(num):
  859.     val = ''
  860.     AP = 'ABCDEFGHIJKLMNOP'
  861.     num = int(abs(num))
  862.     while num:
  863.         (num, mod) = divmod(num, 16)
  864.         val = AP[mod] + val
  865.     return val
  866.  
  867.  
  868. def ParseFlags(resp):
  869.     mo = Flags.match(resp)
  870.     if not mo:
  871.         return ()
  872.     
  873.     return tuple(mo.group('flags').split())
  874.  
  875.  
  876. def Time2Internaldate(date_time):
  877.     if isinstance(date_time, (int, float)):
  878.         tt = time.localtime(date_time)
  879.     elif isinstance(date_time, (tuple, time.struct_time)):
  880.         tt = date_time
  881.     elif isinstance(date_time, str) and (date_time[0], date_time[-1]) == ('"', '"'):
  882.         return date_time
  883.     else:
  884.         raise ValueError('date_time not of a known type')
  885.     dt = time.strftime('%d-%b-%Y %H:%M:%S', tt)
  886.     if dt[0] == '0':
  887.         dt = ' ' + dt[1:]
  888.     
  889.     if time.daylight and tt[-1]:
  890.         zone = -(time.altzone)
  891.     else:
  892.         zone = -(time.timezone)
  893.     return '"' + dt + ' %+03d%02d' % divmod(zone // 60, 60) + '"'
  894.  
  895. if __name__ == '__main__':
  896.     import getopt
  897.     import getpass
  898.     
  899.     try:
  900.         (optlist, args) = getopt.getopt(sys.argv[1:], 'd:s:')
  901.     except getopt.error:
  902.         val = None
  903.         (optlist, args) = ((), ())
  904.  
  905.     stream_command = None
  906.     for opt, val in optlist:
  907.         if opt == '-d':
  908.             Debug = int(val)
  909.         elif opt == '-s':
  910.             stream_command = val
  911.             if not args:
  912.                 args = (stream_command,)
  913.             
  914.         
  915.     
  916.     if not args:
  917.         args = ('',)
  918.     
  919.     host = args[0]
  920.     USER = getpass.getuser()
  921.     if not host:
  922.         pass
  923.     PASSWD = getpass.getpass('IMAP password for %s on %s: ' % (USER, 'localhost'))
  924.     test_mesg = 'From: %(user)s@localhost%(lf)sSubject: IMAP4 test%(lf)s%(lf)sdata...%(lf)s' % {
  925.         'user': USER,
  926.         'lf': '\n' }
  927.     test_seq1 = (('login', (USER, PASSWD)), ('create', ('/tmp/xxx 1',)), ('rename', ('/tmp/xxx 1', '/tmp/yyy')), ('CREATE', ('/tmp/yyz 2',)), ('append', ('/tmp/yyz 2', None, None, test_mesg)), ('list', ('/tmp', 'yy*')), ('select', ('/tmp/yyz 2',)), ('search', (None, 'SUBJECT', 'test')), ('fetch', ('1', '(FLAGS INTERNALDATE RFC822)')), ('store', ('1', 'FLAGS', '(\\Deleted)')), ('namespace', ()), ('expunge', ()), ('recent', ()), ('close', ()))
  928.     test_seq2 = (('select', ()), ('response', ('UIDVALIDITY',)), ('uid', ('SEARCH', 'ALL')), ('response', ('EXISTS',)), ('append', (None, None, None, test_mesg)), ('recent', ()), ('logout', ()))
  929.     
  930.     def run(cmd, args):
  931.         M._mesg('%s %s' % (cmd, args))
  932.         (typ, dat) = getattr(M, cmd)(*args)
  933.         M._mesg('%s => %s %s' % (cmd, typ, dat))
  934.         if typ == 'NO':
  935.             raise dat[0]
  936.         
  937.         return dat
  938.  
  939.     
  940.     try:
  941.         if stream_command:
  942.             M = IMAP4_stream(stream_command)
  943.         else:
  944.             M = IMAP4(host)
  945.         if M.state == 'AUTH':
  946.             test_seq1 = test_seq1[1:]
  947.         
  948.         M._mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
  949.         M._mesg('CAPABILITIES = %r' % (M.capabilities,))
  950.         for cmd, args in test_seq1:
  951.             run(cmd, args)
  952.         
  953.         for ml in run('list', ('/tmp/', 'yy%')):
  954.             mo = re.match('.*"([^"]+)"$', ml)
  955.             if mo:
  956.                 path = mo.group(1)
  957.             else:
  958.                 path = ml.split()[-1]
  959.             run('delete', (path,))
  960.         
  961.         for cmd, args in test_seq2:
  962.             dat = run(cmd, args)
  963.             if (cmd, args) != ('uid', ('SEARCH', 'ALL')):
  964.                 continue
  965.             
  966.             uid = dat[-1].split()
  967.             if not uid:
  968.                 continue
  969.             
  970.             run('uid', ('FETCH', '%s' % uid[-1], '(FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822.TEXT)'))
  971.         
  972.         print '\nAll tests OK.'
  973.     except:
  974.         print '\nTests failed.'
  975.         if not Debug:
  976.             print '\nIf you would like to see debugging output,\ntry: %s -d5\n' % sys.argv[0]
  977.         
  978.         raise 
  979.  
  980.  
  981.